use super::define_config_ast::DefineConfigAst;
use super::lib_import::LibImport;
use crate::error::SysError;
use crate::model::{Alias, ServerProxy};
use crate::traits::{
    CommonModuleTrait, CommonModuleUpdatedTrait, ViteConfigTrait, ViteConfigUpdatedModuleTrait,
};
use crate::traits::{ImportDeclTrait, ObjectLitTrait};
use crate::util::{parse_vite_config_ts, write_content_at};
use swc_common::Spanned;
use swc_ecma_ast::*;

#[derive(Debug)]
pub struct CodeModule {
    source_code: String,
    module: Module,
    is_module_updated: bool,
}

#[derive(Debug)]
pub struct UpdatedModule<'a> {
    module: &'a Module,
}

impl<'a> UpdatedModule<'a> {
    pub fn try_get_import(&'a self, lib_name: &str) -> Option<ImportDecl> {
        // 遍历模块中的所有项
        for item in &self.module.body {
            // 检查该项是否为导入声明
            if let ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl)) = item {
                // 检查导入声明的源名称是否等于lib_name
                if import_decl.src.value == lib_name {
                    return Some(import_decl.clone());
                }
            }
        }

        None
    }
}

impl CodeModule {
    pub fn new(source_code: &str) -> Result<Self, Box<dyn std::error::Error>> {
        Ok(Self {
            source_code: source_code.to_string(),
            module: parse_vite_config_ts(source_code)?,
            is_module_updated: true,
        })
    }
    pub fn update_module(&mut self) -> Result<UpdatedModule, Box<dyn std::error::Error>> {
        if !self.is_module_updated {
            self.module = parse_vite_config_ts(&self.source_code)?;
            self.is_module_updated = true;
        }
        Ok(UpdatedModule {
            module: &self.module,
        })
    }
    pub fn is_module_updated(&self) -> bool {
        self.is_module_updated
    }
    pub fn get_source(&self) -> String {
        self.source_code.clone()
    }

    pub fn get_init_pos_of_code(&self) -> usize {
        if let Some(item) = self.module.body.iter().next() {
            match item {
                // 处理其他类型的 ModuleDecl
                ModuleItem::ModuleDecl(decl) => {
                    return (decl.span_lo().0 - 1) as usize;
                }
                // 处理 Stmt 类型
                ModuleItem::Stmt(stmt) => {
                    return (stmt.span_lo().0 - 1) as usize;
                }
            }
        }

        0
    }
}

impl<'a> ViteConfigUpdatedModuleTrait for UpdatedModule<'a> {
    fn get_define_config_ast(&self) -> DefineConfigAst {
        DefineConfigAst::build(self.module)
    }

    fn try_get_server_node(&self) -> Option<ObjectLit> {
        let define_config_ast = self.get_define_config_ast();

        if let Some(arg_object_value) = define_config_ast.arg_object_value() {
            if let Some(server_object) = arg_object_value.try_get_object_value("server") {
                return Some(server_object);
            }
        }
        None
    }

    fn try_get_resolve_node(&self) -> Option<ObjectLit> {
        let define_config_ast = self.get_define_config_ast();

        if let Some(arg_object_value) = define_config_ast.arg_object_value() {
            if let Some(resolve_object) = arg_object_value.try_get_object_value("resolve") {
                return Some(resolve_object);
            }
        }
        None
    }
}

impl<'a> CommonModuleUpdatedTrait<'a> for UpdatedModule<'a> {
    fn try_get_import(&'a self, lib_name: &str) -> Option<ImportDecl> {
        // 遍历模块中的所有项
        for item in &self.module.body {
            // 检查该项是否为导入声明
            if let ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl)) = item {
                // 检查导入声明的源名称是否等于lib_name
                if import_decl.src.value == lib_name {
                    return Some(import_decl.clone());
                }
            }
        }

        None
    }
}

impl CommonModuleTrait for CodeModule {
    fn init_sub_node(
        &mut self,
        parent_node: &Option<ObjectLit>,
        sub_node_name: &str,
        err_msg: &str,
        new_content: &str,
    ) -> Result<(), Box<dyn std::error::Error>> {
        if let Some(node_object) = parent_node {
            if node_object.try_get_object_value(sub_node_name).is_none() {
                self.source_code = write_content_at(
                    &self.source_code,
                    (node_object.span.lo.0) as usize,
                    new_content,
                )?;
                self.is_module_updated = false;
            }
        } else {
            return Err(SysError::msg(err_msg).into());
        }

        Ok(())
    }

    fn init_import(&mut self, import_def: &LibImport) -> Result<(), Box<dyn std::error::Error>> {
        let updated_module = self.update_module()?;

        if let Some(import_decl) = updated_module.try_get_import(&import_def.lib_name) {
            if !import_decl.has_element(import_def) {
                let new_content = import_def.to_code();
                self.source_code =
                    write_content_at(&self.source_code, self.get_init_pos_of_code(), &new_content)?;
                self.is_module_updated = false;
            }
        } else {
            let new_content = import_def.to_code();
            self.source_code =
                write_content_at(&self.source_code, self.get_init_pos_of_code(), &new_content)?;
            self.is_module_updated = false;
        }
        Ok(())
    }
}
impl ViteConfigTrait for CodeModule {
    fn init_server_node(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        self.init_root_node("server")
    }

    fn init_resolve_node(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        self.init_root_node("resolve")
    }

    fn init_root_node(&mut self, node_name: &str) -> Result<(), Box<dyn std::error::Error>> {
        let updated_module = self.update_module()?;

        let define_config_ast = updated_module.get_define_config_ast();

        if let Some(arg_object_value) = define_config_ast.arg_object_value() {
            if arg_object_value.try_get_object_value(node_name).is_none() {
                let new_content = &format!(
                    r#"  "{}": {{}},
"#,
                    node_name
                );
                self.source_code = write_content_at(
                    &self.source_code,
                    (arg_object_value.span.lo.0 + 1) as usize,
                    new_content,
                )?;
                self.is_module_updated = false;
            }
        } else {
            return Err(SysError::msg("未找到defineConfig调用").into());
        }
        Ok(())
    }

    fn init_alias_node(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        let parent_node = &self.update_module()?.try_get_resolve_node();
        self.init_sub_node(
            parent_node,
            "alias",
            "无resolve节点，请先调用init_resolve_node来初始化resolve节点",
            r#"  "alias": {},
"#,
        )
    }

    fn init_proxy_node(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        let parent_node = &self.update_module()?.try_get_server_node();
        self.init_sub_node(
            parent_node,
            "proxy",
            "无server节点，请先调用init_server_node来初始化server节点",
            r#"  "proxy": {},
"#,
        )
    }

    fn insert_proxy(&mut self, proxy_def: &ServerProxy) -> Result<(), Box<dyn std::error::Error>> {
        let updated_module = self.update_module()?;
        if let Some(proxy_object) = updated_module.try_get_proxy_node() {
            let new_content = format!(
                r#"  "{}": {{
  "target": "{}",
  "rewrite": (path)=>path.replace(/^\{}/, '')
    }},
"#,
                proxy_def.route_name, proxy_def.target,proxy_def.route_name
            );
            self.source_code = write_content_at(
                &self.source_code,
                (proxy_object.span.lo.0) as usize,
                &new_content,
            )?;
            self.is_module_updated = false
        } else {
            return Err(SysError::msg("无proxy节点，请调用init_proxy_node初始化").into());
        }

        Ok(())
    }

    fn insert_alias(&mut self, alias_def: &Alias) -> Result<(), Box<dyn std::error::Error>> {
        let updated_module = self.update_module()?;
        if let Some(resolve_object) = updated_module.try_get_alias_node() {
            let new_content = format!(
                r#"
        "{}": resolve(__dirname, '{}'),
      "#,
                alias_def.name, alias_def.path
            );
            self.source_code = write_content_at(
                &self.source_code,
                (resolve_object.span.lo.0) as usize,
                &new_content,
            )?;
            self.is_module_updated = false
        } else {
            return Err(SysError::msg("无proxy节点，请调用init_proxy_node初始化").into());
        }

        Ok(())
    }
}
